芋道 Spring Boot 服务容错 Resilience4j 入门 您所在的位置:网站首页 canal instanceproperties 芋道 Spring Boot 服务容错 Resilience4j 入门

芋道 Spring Boot 服务容错 Resilience4j 入门

2023-05-08 16:41| 来源: 网络整理| 查看: 265

芋道 Spring Boot 服务容错 Resilience4j 入门

总阅读量:次

阅读全文

《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》 《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》 《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》 《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Resilience4j/ 「芋道源码」欢迎转载,保留摘要,谢谢!

1. 概述 2. CircuitBreaker 熔断器 3. RateLimiter 限流器 4. Bulkhead 舱壁 5. Retry 重试 6. TimeLimiter 限时器 7. 执行顺序 8. 监控端点 9. 集成到 Feign 10. 集成到 Dubbo 666. 彩蛋

🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。 新的源码解析文章实时收到通知。每周更新一篇左右。 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-59 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

友情提示:如下关于 Resilience4j 的介绍,英文对应 https://resilience4j.readme.io/docs/getting-started 文档。

Resilience4j 是一个轻量级的容错组件,其灵感来自于 Hystrix,但主要为 Java 8 和函数式编程所设计。

轻量级体现在其只用 Vavr 库,没有任何外部依赖。而 Hystrix 依赖了 Archaius,Archaius 本身又依赖很多第三方包,例如 Guava、Apache Commons Configuration 等等。

友情提示:如果胖友对服务容错、服务降级、服务雪崩、熔断器等概念不是很熟悉,建议先看看《芋道 Spring Boot 服务容错 Hystrix 入门》文章的「1. 概述」小节。

一来是这些概念面试容易问,二来整清楚为什么要使用容错组件。

要使用 Resilience4j,不需要引入所有依赖,只需要引入你所需的模块。

Resilience4j 项目目录

友情提示:模块比较丰富,重要的艿艿已经加粗。

Core modules 核心模块

resilience4j-circuitbreaker: Circuit breaking 熔断器

resilience4j-ratelimiter: Rate limiting 限流

resilience4j-bulkhead: Bulkheading 舱壁隔离

resilience4j-retry: Automatic retrying (sync and async) 自动重试(同步和异步)

resilience4j-cache: Result caching 响应缓存

看了下官方文档,Resilience4j 提供的 Cache 功能,指的是针对 JSR 107 JCACHE 的集成,和 Hystrix 的请求缓存不是一个东东。

从 ISSUE#815 的讨论,也可以看出,就是针对 JCACHE 的容错。

resilience4j-timelimiter: Timeout handling 超时处理

Add-on modules resilience4j-retrofit: Retrofit adapter resilience4j-feign: Feign adapter resilience4j-consumer: Circular Buffer Event consumer resilience4j-kotlin: Kotlin coroutines support Frameworks modules resilience4j-spring-boot: Spring Boot Starter resilience4j-spring-boot2: Spring Boot 2 Starter resilience4j-ratpack: Ratpack Starter resilience4j-vertx: Vertx Future decorator Reactive modules resilience4j-rxjava2: Custom RxJava2 operators resilience4j-reactor: Custom Spring Reactor operators Metrics modules resilience4j-micrometer: Micrometer Metrics exporter resilience4j-metrics: Dropwizard Metrics exporter resilience4j-prometheus: Prometheus Metrics exporter

下面,我们开始愉快的入门吧~

2. CircuitBreaker 熔断器

示例代码对应仓库:

用户服务:lab-59-user-service Resilience4j 示例项目:lab-59-resilience4j-demo01

CircuitBreaker 一共有 CLOSED、OPEN、HALF_OPEN 三种状态,通过状态机实现。转换关系如下图所示:

Circuitbreaker 状态机

当熔断器关闭(CLOSED)时,所有的请求都会通过熔断器。 如果失败率超过设定的阈值,熔断器就会从关闭状态转换到打开(OPEN)状态,这时所有的请求都会被拒绝。 当经过一段时间后,熔断器会从打开状态转换到半开(HALF_OPEN)状态,这时仅有一定数量的请求会被放入,并重新计算失败率。如果失败率超过阈值,则变为打开(OPEN)状态;如果失败率低于阈值,则变为关闭(CLOSE)状态。

友情提示:下面会涉及到 CircuitBreaker 如何实现,可以稍微看看,简单理解。想要深入的胖友,可以阅读《Resilience4j 源码解析》中 CircuitBreaker 部分。

Resilience4j 记录请求状态的数据结构和 Hystrix 不同:Hystrix 是使用滑动窗口来进行存储的,而 Resilience4j 采用的是 Ring Bit Buffer(环形缓冲区)。Ring Bit Buffer 在内部使用 BitSet 这样的数据结构来进行存储,结构如下图所示:

环形缓冲区

每一次请求的成功或失败状态只占用一个 bit 位,与 boolean 数组相比更节省内存。BitSet 使用 long[] 数组来存储这些数据,意味着 16 个值(64 bit)的数组可以存储 1024 个调用状态。

**计算失败率需要填满环形缓冲区。**例如,如果环形缓冲区的大小为 10,则必须至少请求满 10 次,才会进行故障率的计算。如果仅仅请求了 9 次,即使 9 个请求都失败,熔断器也不会打开。但是 CLOSE 状态下的缓冲区大小设置为 10 并不意味着只会进入 10 个请求,在熔断器打开之前的所有请求都会被放入。

当故障率高于设定的阈值时,熔断器状态会从由 CLOSE 变为 OPEN。这时所有的请求都会抛出 CallNotPermittedException 异常。 当经过一段时间后,熔断器的状态会从 OPEN 变为 HALF_OPEN。HALF_OPEN 状态下同样会有一个 Ring Bit Buffer,用来计算HALF_OPEN 状态下的故障率。如果高于配置的阈值,会转换为 OPEN,低于阈值则装换为 CLOSE。与 CLOSE 状态下的缓冲区不同的地方在于,HALF_OPEN 状态下的缓冲区大小会限制请求数,只有缓冲区大小的请求数会被放入。

除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问)和 FORCED_OPEN(始终拒绝访问)。这两个状态不会生成熔断器事件(除状态装换外),并且不会记录事件的成功或者失败。退出这两个状态的唯一方法是触发状态转换或者重置熔断器。

本小节,我们来搭建一个 Feign 的快速入门示例。步骤如下:

首先,使用 SpringMVC 搭建一个用户服务,提供 JSON 数据格式的 HTTP API。 然后,搭建一个使用 Feign 声明式调用用户服务 HTTP API 的示例项目。

本小节,我们来搭建一个 Resilience4j CircuitBreaker 的快速入门示例。步骤如下:

首先,搭建一个 user-service 用户服务,提供获取用户信息的 HTTP API 接口。 然后,搭建一个用户服务的消费者,使用 Resilience4j 实现服务容错。 2.1 搭建用户服务

创建 lab-59-user-service 项目,搭建用户服务。代码如下图所示:

项目代码

比较简单,主要是提供 http://127.0.0.1:18080/user/get 接口,获取用户详情。具体的代码,肯定不用艿艿啰嗦讲解哈,点击 lab-59-user-service 查看。

2.2 搭建 Resilience4j 示例项目

创建 lab-59-resilience4j-demo01 项目,搭建一个用户服务的消费者,使用 Resilience4j 实现服务容错。代码如下图所示:

项目代码

2.2.1 引入依赖

创建 pom.xml 文件,引入 Resilience4j 相关依赖。

lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-59-resilience4j-demo01 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.resilience4j resilience4j-spring-boot2 1.4.0 org.aspectj aspectjrt 1.9.5 org.aspectj aspectjweaver 1.9.5

① 添加 resilience4j-spring-boot2 依赖,引入 Resilience4j Starter 相关依赖,并实现对其的自动配置。

② 添加 aspectjrt 和 aspectjweaver 依赖,引入 Aspectj 依赖,支持 AOP 相关注解、表达式等等。因为 Resilience4j 实现其功能,可以通过注解 + AOP的组合,而 AOP 的功能是基于 Aspectj 工具库来实现的。

2.2.2 配置文件

创建 application.yml 配置文件,添加 Resilience4j CircuitBreaker 相关配置项。

resilience4j: # Resilience4j 的熔断器配置项,对应 CircuitBreakerProperties 属性类 circuitbreaker: instances: backendA: failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值,单位:百分比。默认为 50 ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100 ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小,会限制线程的并发量。例如,缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10 wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间,单位:微秒 automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true,当等待时间结束会自动由打开变为半开;若置为 false,则需要一个请求进入来触发熔断器状态转换。默认为 true register-health-indicator: true # 是否注册到健康监测

① resilience4j.circuitbreaker 是 Resilience4j 的熔断器配置项,对应 CircuitBreakerProperties 属性类。

② 在 resilience4j.circuitbreaker.instances 配置项下,可以添加熔断器实例的配置,其中 key 为熔断器实例的名字,value 为熔断器实例的具体配置,对应 CircuitBreakerConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendA" 的熔断器,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.circuitbreaker.configs 配置项下,可以添加通用配置项,提供给 resilience4j.circuitbreaker.instances 熔断器使用。示例如下图:

 示例

2.2.3 DemoController

创建 DemoController 类,提供调用用户服务的 HTTP API 接口。代码如下:

@RestController@RequestMapping("/demo")public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @CircuitBreaker(name = "backendA", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; }}

① 在 #getUser(Integer id) 方法中,我们使用 RestTemplate 调用用户服务提供的 /user/get 接口,获取用户详情。

② 在 #getUser(Integer id) 方法上,添加了 Resilience4j 提供的 @CircuitBreaker 注解:

通过 name 属性,设置对应的 CircuitBreaker 熔断器实例名为 "backendA",就是我们在「2.2.2 配置文件」中所添加的。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。

通过不同的 Throwable 异常,我们可以进行不同的 fallback 降级处理。极端情况下,Resilience4j 熔断器打开时,不会执行 #getUser(Integer id) 方法,而是直接抛出 CallNotPermittedException 异常,然后也是进入 fallback 降级处理。

2.2.4 DemoApplication

创建 DemoApplication 类,作为 Resilience4j 示例项目的启动类。代码如下:

@SpringBootApplicationpublic class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

2.3 简单测试

执行 UserServiceApplication 启动用户服务,执行 DemoApplication 启动 Resilience4j 示例项目。

① 使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,成功调用用户服务,返回结果为 User:1。

② 停止 UserServiceApplication 关闭用户服务。

使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,失败调用用户服务,返回结果为 mock:User:1。

此时我们会看到如下日志,可以判断触发 Resilience4j 的 fallback 服务降级的方法。

2020-05-19 08:46:44.094 INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]2020-05-19 08:46:44.119 INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]

③ 疯狂使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,会触发 Hystrix 熔断器熔断(打开),不再执行 #getUser(Integer id) 方法,而是直接 fallback 触发 #getUserFallback(Integer id, Throwable throwable) 方法。日志内容如下:

2020-05-19 08:47:03.107 INFO 65728 --- [nio-8080-exec-7] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]2020-05-19 08:47:03.204 INFO 65728 --- [nio-8080-exec-8] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]2020-05-19 08:47:03.275 INFO 65728 --- [nio-8080-exec-9] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]

④ 重新执行 UserServiceApplication 启动用户服务。

使用浏览器,多次访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,Hystrix 熔断器的状态逐步从打开 => 半开 => 关闭。日志内容如下:

// 打开2020-05-19 08:47:39.408 INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]...// 半开2020-05-19 08:48:23.095 INFO 65728 --- [nio-8080-exec-5] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]// 关闭2020-05-19 08:48:32.488 INFO 65728 --- [nio-8080-exec-6] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]2020-05-19 08:48:32.666 INFO 65728 --- [nio-8080-exec-7] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]2020-05-19 08:48:33.230 INFO 65728 --- [nio-8080-exec-9] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]...

3. RateLimiter 限流器

示例代码对应仓库:

Resilience4j 示例项目:lab-59-resilience4j-demo01

RateLimiter 一共有两种实现类:

AtomicRateLimiter:基于令牌桶限流算法实现限流。 SemaphoreBasedRateLimiter:基于 Semaphore 实现限流。

默认情况下,采用 AtomicRateLimiter 基于令牌桶限流算法实现限流。= = 搜了一圈 Resilience4j 的源码,貌似 SemaphoreBasedRateLimiter 没有地方在使用,难道已经被抛弃了~

FROM 《接口限流实践》

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 限流器相关的示例代码。改动点如下图所示:

项目调整

3.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j RateLimiter 相关配置项。

resilience4j: # Resilience4j 的限流器配置项,对应 RateLimiterProperties 属性类 ratelimiter: instances: backendB: limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 limit-refresh-period: 10s # 每个周期的时长,单位:微秒。默认为 500 timeout-duration: 5s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s register-health-indicator: true # 是否注册到健康监测

① resilience4j.ratelimiter 是 Resilience4j 的 RateLimiter 配置项,对应 RateLimiterConfigurationProperties 属性类。

② 在 resilience4j.ratelimiter.instances 配置项下,可以添加限流器实例的配置,其中 key 为限流器实例的名字,value 为限流器实例的具体配置,对应 RateLimiterConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendB" 的限流器,具体的每个项,胖友看看艿艿添加在后面的注释哈。有一点要注意,在请求被限流时,并不是直接失败抛出异常,而是阻塞等待最大 timeout-duration 微秒,看看是否能够请求通过。

③ 在 resilience4j.ratelimiter.configs 配置项下,可以添加通用配置项,提供给 resilience4j.ratelimiter.instances 限流器使用。示例如下图:

 示例

3.2 RateLimiterDemoController

创建 RateLimiterDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController@RequestMapping("/rate-limiter-demo")public class RateLimiterDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @RateLimiter(name = "backendB", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; }}

① 在 #getUser(Integer id) 方法中,我们直接返回 "User:{id}",不进行任何逻辑。

② 在 #getUser(Integer id) 方法上,添加了 Resilience4j 提供的 @RateLimiter 注解:

通过 name 属性,设置对应的 RateLimiter 实例名为 "backendB",就是我们在「3.1 配置文件」中所添加的。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。

在请求被限流时,Resilience4j 不会执行 #getUser(Integer id) 方法,而是直接抛出 RequestNotPermitted 异常,然后就进入 fallback 降级处理。

友情提示:注意,@RateLimiter 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被限流时抛出的 RequestNotPermitted 异常,也处理 #getUser(Integer id) 方法执行时的普通异常。

3.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

① 使用浏览器,访问 http://127.0.0.1:8080/rate-limiter-demo/get_user?id=1 地址,成功返回结果为 User:1。

② 立马使用浏览器再次访问,会阻塞等待 < 5 秒左右,降级返回 mock:User:1。同时,我们在 IDEA 控制台的日志中,可以看到被限流时抛出的 RequestNotPermitted 异常。

2020-05-19 21:50:42.585 INFO 79815 --- [nio-8080-exec-1] c.i.s.l.r.c.RateLimiterDemoController : [getUserFallback][id(1) exception(RequestNotPermitted)]

另外,我们将 @RateLimiter 和 @CircuitBreaker 注解添加在相同方法上,进行组合使用,来实现限流和断路的作用。但是要注意,需要添加 resilience4j.circuitbreaker.instances..ignoreExceptions=io.github.resilience4j.ratelimiter.RequestNotPermitted 配置项,忽略限流抛出的 RequestNotPermitted 异常,避免触发断路器的熔断。

4. Bulkhead 舱壁

示例代码对应仓库:

Resilience4j 示例项目:lab-59-resilience4j-demo01

Bulkhead 指的是船舶中的舱壁,它将船体分隔为多个船舱,在船部分受损时可避免沉船。

在 Resilience4j 中,提供了基于 Semaphore 信号量和 ThreadPool 线程池两种 Bulkhead 实现,隔离不同种类的调用,并提供流控的能力,从而避免某类调用异常时而占用所有资源,导致影响整个系统。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 舱壁相关的示例代码。改动点如下图所示:

项目调整

良心的艿艿,两种类型的 Bulkhead 使用示例都提供了噢。

友情提示:我们先来看信号量类型的 Bulkhead 示例。 友情提示:我们先来看信号量类型的 Bulkhead 示例。 友情提示:我们先来看信号量类型的 Bulkhead 示例。

4.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j 信号量类型的 Bulkhead 相关配置项。

resilience4j: # Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadConfigurationProperties 属性类 bulkhead: instances: backendC: max-concurrent-calls: 1 # 并发调用数。默认为 25 max-wait-duration: 5s # 并发调用到达上限时,阻塞等待的时长,单位:微秒。默认为 0

① resilience4j.bulkhead 是 Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadProperties 属性类。

② 在 resilience4j.bulkhead.instances 配置项下,可以添加 Bulkhead 实例的配置,其中 key 为 Bulkhead 实例的名字,value 为 Bulkhead 实例的具体配置,对应 BulkheadProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendC" 的 Bulkhead,具体的每个项,胖友看看艿艿添加在后面的注释哈。有一点要注意,在请求被流控时,并不是直接失败抛出异常,而是阻塞等待最大 max-wait-duration 微秒,看看是否能够请求通过。

友情提示:这里我们设置 max-concurrent-calls 配置项为 1,仅允许并发调用数为 1,方便测试。

③ 在 resilience4j.bulkhead.configs 配置项下,可以添加通用配置项,提供给 resilience4j.bulkhead.instances Bulkhead 使用。示例如下图:

 示例

4.2 BulkheadDemoController

创建 BulkheadDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController@RequestMapping("/bulkhead-demo")public class BulkheadDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @Bulkhead(name = "backendC", fallbackMethod = "getUserFallback", type = Bulkhead.Type.SEMAPHORE) public String getUser(@RequestParam("id") Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; }}

① 在 #getUser(Integer id) 方法中,我们直接返回 "User:{id}",不进行任何逻辑。不过,这里为了模拟调用执行一定时长,通过 sleep 10 秒来实现。

② 在 #getUser(Integer id) 方法上,添加了 Resilience4j 提供的 @Bulkhead 注解:

通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.1 配置文件」中所添加的。 通过 type 属性,设置 Bulkhead 类型为信号量的方式。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。

在请求被流控时,Resilience4j 不会执行 #getUser(Integer id) 方法,而是直接抛出 BulkheadFullException 异常,然后就进入 fallback 降级处理。

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser(Integer id) 方法执行时的普通异常。

4.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

① 使用浏览器,访问 http://127.0.0.1:8080/bulkhead-demo/get_user?id=1 地址,成功返回结果为 User:1。

② 立马使用浏览器再次访问,会阻塞等待 < 5 秒左右,降级返回 mock:User:1。同时,我们在 IDEA 控制台的日志中,可以看到被流控时抛出的 BulkheadFullException 异常。

2020-05-20 07:39:17.181 INFO 84230 --- [nio-8080-exec-2] c.i.s.l.r.c.BulkheadDemoController : [getUserFallback][id(1) exception(BulkheadFullException)]

友情提示:我们先来看线程池类型的 Bulkhead 示例。 友情提示:我们先来看线程池类型的 Bulkhead 示例。 友情提示:我们先来看线程池类型的 Bulkhead 示例。

4.4 配置文件

修改 application.yml 配置文件,添加 Resilience4j 信号量类型的 Bulkhead 相关配置项。

resilience4j: # Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类 thread-pool-bulkhead: instances: backendD: max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors() core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1 queue-capacity: 100 # 线程池的队列大小。默认为 100 keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒

① resilience4j.thread-pool-bulkhead 是 Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类。

② 在 resilience4j.thread-pool-bulkhead.instances 配置项下,可以添加 Bulkhead 实例的配置,其中 key 为 Bulkhead 实例的名字,value 为 Bulkhead 实例的具体配置,对应 ThreadPoolBulkheadProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendD" 的 Bulkhead,具体的每个项,胖友看看艿艿添加在后面的注释哈。

友情提示:这里我们设置 max-thread-pool-size 配置项为 1,仅允许并发调用数为 1,方便测试。

③ 在 resilience4j.thread-pool-bulkhead.configs 配置项下,可以添加通用配置项,提供给 resilience4j.thread-pool-bulkhead.instances Bulkhead 使用。示例如下图:

 示例

4.5 ThreadPoolBulkheadDemoController

创建 ThreadPoolBulkheadDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController@RequestMapping("/thread-pool-bulkhead-demo")public class ThreadPoolBulkheadDemoController { @Autowired private ThreadPoolBulkheadService threadPoolBulkheadService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { threadPoolBulkheadService.getUser0(id); return threadPoolBulkheadService.getUser0(id).get(); } @Service public static class ThreadPoolBulkheadService { private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class); @Bulkhead(name = "backendD", fallbackMethod = "getUserFallback", type = Bulkhead.Type.THREADPOOL) public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } }}

友情提示:这里创建了 ThreadPoolBulkheadService 的原因是,这里我们使用 Resilience4j 是基于注解 + AOP的方式,如果直接 this. 方式来调用方法,实际没有走代理,导致 Resilience4j 无法使用 AOP。

① 在 #getUser(Integer id) 方法中,我们调用了 2 次 ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法,测试在线程池 Bulkhead 下,且线程池大小为 1 时,被流控成“串行”执行。

② 在 #getUser0(Integer id) 方法上,添加了 Resilience4j 提供的 @Bulkhead 注解:

通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.4 配置文件」中所添加的。 通过 type 属性,设置 Bulkhead 类型为线程池的方式。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。 注意!!!方法的返回类型必须是 CompletableFuture 类型,包括 fallback 方法,否则会报异常,毕竟要提交线程池中执行。

在请求被流控时,Resilience4j 不会执行 #getUser0(Integer id) 方法,而是直接抛出 BulkheadFullException 异常,然后就进入 fallback 降级处理。不过艿艿测试了很久,都没触发抛出 BulkheadFullException 异常的情况,但是看 Resilience4j 源码又有这块逻辑,苦闷~

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser0(Integer id) 方法执行时的普通异常。

4.6 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/thread-pool-bulkhead-demo/get_user?id=1 接口,可以通过日志看出,ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法是“串行”执行的:

2020-05-20 8:25:24.059 INFO 85835 --- [head-backendD-1] DemoController$ThreadPoolBulkheadService : [getUser][id(1)]2020-05-20 8:25:34.066 INFO 85835 --- [head-backendD-1] DemoController$ThreadPoolBulkheadService : [getUser][id(1)]

相互之间间隔 10 秒,因为我们在 #getUser0(Integer id) 方法是 sleep 了 10 秒。

5. Retry 重试

示例代码对应仓库:

Resilience4j 示例项目:lab-59-resilience4j-demo01

Resilience4j 提供了 Retry 组件,在执行失败时,进行重试的行为。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 重试相关的示例代码。改动点如下图所示:

项目调整

5.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j Retry 相关配置项。

resilience4j: # Resilience4j 的重试 Retry 配置项,对应 RetryProperties 属性类 retry: instances: backendE: max-retry-Attempts: 3 # 最大重试次数。默认为 3 wait-duration: 5s # 下次重试的间隔,单位:微秒。默认为 500 毫秒 retry-exceptions: # 需要重试的异常列表。默认为空 ingore-exceptions: # 需要忽略的异常列表。默认为空

① resilience4j.retry 是 Resilience4j 的 Retry 配置项,对应 RetryProperties 属性类。

② 在 resilience4j.retry.instances 配置项下,可以添加 Retry 实例的配置,其中 key 为 Retry 实例的名字,value 为 Retry 实例的具体配置,对应 RetryProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendE" 的 Retry,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.retry.configs 配置项下,可以添加通用配置项,提供给 resilience4j.retry.instances Retry 使用。示例如下图:

 示例

5.2 RetryDemoController

创建 RetryDemoController 类,提供调用用户服务的 HTTP API 接口。代码如下:

@RestController@RequestMapping("/retry-demo")public class RetryDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @Retry(name = "backendE", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; }}

① 在 #getUser(Integer id) 方法中,我们使用 RestTemplate 调用用户服务提供的 /user/get 接口,获取用户详情。

② 在 #getUser(Integer id) 方法上,添加了 Resilience4j 提供的 @Retry 注解:

通过 name 属性,设置对应的 Retry 实例名为 "backendE",就是我们在「5.1 配置文件」中所添加的。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。

在多次重试失败到达上限时,Resilience4j 会抛出 MaxRetriesExceeded 异常,然后就进入 fallback 降级处理。

5.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/retry-demo/get_user?id=1 接口,可以通过日志看出,一共执行 + 重试共 3 次,每次间隔 5 秒:

// 执行 + 重试共 3 次2020-05-20 20:07:34.693 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]2020-05-20 20:07:39.722 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]2020-05-20 20:07:44.727 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]// 最终 fallback2020-05-20 20:07:44.729 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUserFallback][id(1) exception(ResourceAccessException)]

6. TimeLimiter 限时器

示例代码对应仓库:

Resilience4j 示例项目:lab-59-resilience4j-demo01

Resilience4j 提供了 TimeLimiter 组件,限制任务的执行时长,在超过时抛出异常。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 限时器相关的示例代码。改动点如下图所示:

项目调整

6.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j TimeLimiter 相关配置项。

resilience4j: # Resilience4j 的超时限制器 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类 timelimiter: instances: backendF: timeout-duration: 1s # 等待超时时间,单位:微秒。默认为 1 秒 cancel-running-future: true # 当等待超时时,是否关闭取消线程。默认为 true # Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类 thread-pool-bulkhead: instances: backendD: max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors() core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1 queue-capacity: 200 # 线程池的队列大小。默认为 100 keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒

友情提示:因为 TimeLimiter 需要搭配线程池类型的 Bulkhead,所以这里添加了 resilience4j.thread-pool-bulkhead 配置项,因为 TimeLimiter 是基于线程池来实现超时限制的。

① resilience4j.timelimiter 是 Resilience4j 的 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类。

② 在 resilience4j.timelimiter.instances 配置项下,可以添加 Retry 实例的配置,其中 key 为 TimeLimiter 实例的名字,value 为 TimeLimiter 实例的具体配置,对应 TimeLimiterConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendF" 的 TimeLimiter,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.timelimiter.configs 配置项下,可以添加通用配置项,提供给 resilience4j.timelimiter.instances Retry 使用。暂无示例,和其它配置项是类似的。

6.2 TimeLimiterDemoController

创建 TimeLimiterDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController@RequestMapping("/time-limiter-demo")public class TimeLimiterDemoController { @Autowired private TimeLimiterService timeLimiterService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { return timeLimiterService.getUser0(id).get(); } @Service public static class TimeLimiterService { private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class); @Bulkhead(name = "backendD", type = Bulkhead.Type.THREADPOOL) @TimeLimiter(name = "backendF", fallbackMethod = "getUserFallback") public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } }}

友情提示:这里创建了 TimeLimiterService 的原因是,这里我们使用 Resilience4j 是基于注解 + AOP的方式,如果直接 this. 方式来调用方法,实际没有走代理,导致 Resilience4j 无法使用 AOP。

① 在 #getUser(Integer id) 方法中,直接调用 ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法进行返回。

② 在 #getUser0(Integer id) 方法上,添加了 Resilience4j 提供的 @TimeLimiter 注解:

通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.4 配置文件」中所添加的。 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常。 注意!!!方法的返回类型必须是 CompletableFuture 类型,包括 fallback 方法,否则会报异常,毕竟要提交线程池中执行。

在请求执行超时时,Resilience4j 会抛出 TimeoutException 异常,然后就进入 fallback 降级处理。

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser0(Integer id) 方法执行时的普通异常。

③ 在 #getUser0(Integer id) 方法上,添加了 Resilience4j 提供的 @Bulkhead 注解,并设置类型为线程池,原因上文我们也解释过了。

6.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/time-limiter-demo/get_user?id=1 接口,可以通过日志看出,看到执行超时抛出 TimeoutException 异常,而后进入 fallback 服务降级,因此最终返回结果为 mock:User:1。

2020-05-20 23:35:50.633 INFO 93020 --- [head-backendD-1] LimiterDemoController$TimeLimiterService : [getUser][id(1)]2020-05-20 23:35:51.637 INFO 93020 --- [pool-2-thread-1] LimiterDemoController$TimeLimiterService : [getUserFallback][id(1) exception(TimeoutException)]

7. 执行顺序

我们在相同方法上,添加上述 Resilience4j 的注解,从而组合使用熔断、限流、舱壁、重试、重试的功能。例如说:

@CircuitBreaker(name = BACKEND, fallbackMethod = "fallback")@RateLimiter(name = BACKEND)@Bulkhead(name = BACKEND)@Retry(name = BACKEND, fallbackMethod = "fallback")@TimeLimiter(name = BACKEND)public String method(String param1) { throws new Exception("xxxx");}private String fallback(String param1, IllegalArgumentException e) { return "test:IllegalArgumentException";}private String fallback(String param1, RuntimeException e) { return "test:RuntimeException";}

此时,我们就要注意它们的执行顺序是 :

Retry > Bulkhead > RateLimiter > TimeLimiter > Bulkhead

想要深入的胖友,可以进一步看看每个注解对应的切面实现,整理如下表格:

注解 切面 顺序 @Retry RetryAspect Ordered.LOWEST_PRECEDENCE - 4 @CircuitBreaker CircuitBreakerAspect Ordered.LOWEST_PRECEDENCE - 3 @RateLimiter RateLimiterAspect Ordered.LOWEST_PRECEDENCE - 2 @TimeLimiter TimeLimiterAspect Ordered.LOWEST_PRECEDENCE - 1 @Bulkhead BulkheadAspect Ordered.LOWEST_PRECEDENCE

当然,我们也可以通过 **-order 配置项,进行自定义执行顺序。修改方式如下图:

修改顺序

8. 监控端点

resilience4j-spring-boot2 基于 Spring Boot Actuator,提供了 Resilience4j 自定义监控端点,如下图所示:

Resilience4j 监控端点

主要分成三种类型:

Metrics endpoint:纯 Endpoint 结尾。 Health endpoint:HealthIndicator 结尾。 Events endpoint:EventsEndpoint 结尾。

友情提示:对 Spring Boot Actuator 不了解的胖友,可以后续阅读《芋道 Spring Boot 监控端点 Actuator 入门》文章。

下面,我们从上述小节的 lab-59-resilience4j-demo01 项目,复制出 lab-59-resilience4j-actuator 项目,接入 Spring Boot Actuator,实现监控相关功能。改动点如下图:

项目改动点

8.1 引入依赖

在 pom.xml 文件中,额外引入 Spring Boot Actuator 相关依赖。代码如下:

org.springframework.boot spring-boot-starter-actuator

8.2 配置文件

修改 application.yaml 配置文件,增加 Spring Boot Actuator 配置项。配置如下:

management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 # 健康检查配置项 health: circuitbreakers.enabled: true ratelimiters.enabled: true

① 关于 Actuator 的配置项的作用,胖友看下艿艿添加的注释。如果还不理解的话,后续看下《芋道 Spring Boot 监控端点 Actuator 入门》文章。

② 默认情况下,Resilience4j 的 CircuitBreakersHealthIndicator 和 RateLimitersHealthIndicator 是关闭的,所以需要设置 management.health.circuitbreakers.enabled 和 management.health.ratelimiters.enabled 配置项为 true。

同时,需要设置对应的 CircuitBreaker 和 RateLimiter 注册到 HealthIndicator 中。如下图所示:

HealthIndicator 注册

下面,我们执行 DemoApplication 启动项目。

8.3 Metrics endpoint

对应 《Resilience4j Metrics endpoint》 文档。

① 访问 http://127.0.0.1:8080/actuator/metrics 端点,可以看到 Resilience4j 提供的所有 Metrics 监控指标。如下图所示:

Resilience4j Metrics

② 访问 http://127.0.0.1:8080/actuator/metrics/resilience4j.bulkhead.core.thread.pool.size 地址,查看 resilience4j.bulkhead.core.thread.pool.size 监控指标,返回结果如下:

{ "name": "resilience4j.bulkhead.core.thread.pool.size", "description": "The core thread pool size", "baseUnit": null, "measurements": [ { "statistic": "VALUE", "value": 1 } ], "availableTags": [ { "tag": "name", "values": [ "backendD" ] } ]}

③ 一般情况下,我们可以通过 Prometheus 采集 Resilience4j 提供的 Metrics 监控数据,然后使用 Grafana 制作监控仪表盘。

友情提示:对 Prometheus 和 Grafana 不了解的胖友,可以阅读《芋道 Prometheus + Grafana + Alertmanager 极简入门》文章。

同时,官方已经提供了 Grafana 的集成示范,可见 grafana_dashboard.json。另外,这里在推荐两篇外国小哥的文章:

《Creating Fault Tolerant Services Using Resilience4j》 《Resilience for Java microservices. Circuit Breaker with Resilience4j》 8.4 Health endpoint

对应 《Resilience4j Health endpoint》 文档。

访问 http://127.0.0.1:8080/actuator/health 端点,可以看到 CircuitBreaker 和 RateLimiter 的健康状态。结果如下:

{ "status": "UP", "components": { "circuitBreakers": { "status": "UP", "details": { "backendA": { "status": "UP", "details": { "failureRate": "-1.0%", "failureRateThreshold": "50.0%", "slowCallRate": "-1.0%", "slowCallRateThreshold": "100.0%", "bufferedCalls": 0, "slowCalls": 0, "slowFailedCalls": 0, "failedCalls": 0, "notPermittedCalls": 0, "state": "CLOSED" } } } }, "rateLimiters": { "status": "UP", "details": { "backendB": { "status": "UP", "details": { "availablePermissions": 1, "numberOfWaitingThreads": 0 } } } }, // ... 省略其它 }}

8.5 Events endpoint

对应 《Resilience4j Events endpoint》 文档。

① 访问 http://127.0.0.1:8080/actuator/ 接口,可以看到每个类型的 Resilience4j 组件,都有自己的 Events endpoint。如下图所示:

Events endpoint

② 我们来测试下 CircuitBreaker 的 Event 事件。

首先,请求 http://127.0.0.1:8080/demo/get_user?id=1 接口,触发一次 fallback 降级,因为我们没有启动用户服务,所以调用显然会失败。

然后,请求 http://127.0.0.1:8080/actuator/circuitbreakerevents 地址,查看 CircuitBreaker 的 Event 事件。返回结果如下:

{ "circuitBreakerEvents": [ { "circuitBreakerName": "backendA", "type": "ERROR", "creationTime": "2020-05-21T07:47:18.168+08:00[Asia/Shanghai]", "errorMessage": "org.springframework.web.client.ResourceAccessException: I/O error on GET request for \"http://127.0.0.1:18080/user/get\": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)", "durationInMs": 32, "stateTransition": null } ]}

9. 集成到 Feign

resilience4j-feign 提供 Resilience4j 针对 Feign 的集成。

比较简单,胖友先自己看看《Resilience4j 官方文档 —— Feign》。

10. 集成到 Dubbo

Resilience4j 暂未提供对 Dubbo 的集成。不过目前 Dubbo 已经提供了两种接入 Resilience4j 实现服务容错的方案:

dubbo-samples-resilience4j-filter 项目:通过 Dubbo Filter 拓展点,实现 Resilience4jCircuitBreakerFilter 接入 CircuitBreaker 熔断器,Resilience4jRateLimiterFilter 接入 RateLimiter 限流器。 dubbo-samples-resilience4j-springboot2 项目:在调用时,进行一层包装,然后添加 Resilience4j 的注解。例如说 CircuitBreakMethodWrapper 或 CircuitBreakTypeWrapper,如下图所示: CircuitBreakMethodWrapper CircuitBreakTypeWrapper

代码不复杂,胖友自己瞅瞅很快就明白了。

666. 彩蛋

至此,我们已经完成了 Resilience4j 的入门。总的来说,因为Resilience4j 几个组件的拆分非常干净,所以理解起来还是蛮轻松的。

后续,胖友可以自己在看看《Resilience4j 官方文档》,进行下查漏补缺。也推荐阅读如下两篇文章:

《Resilience4j —— 轻量级熔断框架》 《Spring Boot 集成 Resilience4j》

另外,也超级推荐阅读下《限流熔断技术选型:从 Hystrix 到 Sentinel》文章,选型上的思考,特别是这张图,好评到不行啊!

Spring Boot


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有